/*
 * Copyright 2015 泛泛o0之辈
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.jfast.framework.upload;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

public class MultiRequestFieldAnalizer {

	public static final String SIGN_FILENAME = "filename=\"";

	public static final String FORM_NAME = "name=\"";

	public static final String QUOTE_FLAG = "\"";

	private final static String CONTENT_TYPE = "Content-Type:";

	private final static String AUTO_LINE = "\r\n\r\n";

	private final static String DEFAULT_ENCODING = "iso-8859-1";

	public static List<byte[]> findFile(byte[] boundary,
			ByteArrayOutputStream buff) throws Exception {

		if (buff.size() < boundary.length * 2) {
			return Collections.emptyList();
		} else {
			byte[] data = buff.toByteArray();// 当前数据

			List<byte[]> fileds = new ArrayList<byte[]>();
			int point = 0;
			int alertStart = 0;
			boolean alert = false;
			int treated = 0;

			while (true) {
				if (!alert && data.length - point < boundary.length * 2) {
					break;
				} else if (alert && data.length - point < boundary.length) {
					break;
				}

				if (byteIsMapper(data, boundary, point)) {
					if (!alert) { // start line
						alert = true;
						alertStart = point + boundary.length;
						point = alertStart;
					} else { // end line
						byte[] filed;
						filed = Arrays.copyOfRange(data, alertStart, point);
						treated += (point - alertStart + boundary.length);

						fileds.add(filed);
						alert = false;
					}
				} else {
					point++;
				}
			}

			if (fileds.size() > 0) {
				buff.reset();
				buff.write(data, treated, data.length - treated);
				return fileds;
			}

			return Collections.emptyList();
		}

	}

	public static List<byte[]> getFilesByteList(List<byte[]> filesByte,
			String charset) throws UnsupportedEncodingException {
		List<byte[]> fieldFilesByte = new ArrayList<byte[]>();
		byte[] fieldName = FORM_NAME.getBytes(charset);
		for (int i = 0; i < filesByte.size(); i++) {
			byte[] bs = filesByte.get(i);
			int point = 0;
			while (true) {
				if (bs.length - point < fieldName.length) {
					break;
				}
				if (byteIsMapper(bs, fieldName, point)) {
					fieldFilesByte.add(bs);
					break;
				} else {
					point++;
				}
			}
		}

		return fieldFilesByte;
	}

	public static UploadFile getUploadFile(byte[] fieldByte, String charset)
			throws UnsupportedEncodingException, IOException {

		UploadFile uploadFile = new UploadFile();

		String fieldName = getFieldParam(fieldByte, charset, FORM_NAME,
				QUOTE_FLAG);

		String fileName = getFieldParam(fieldByte, charset, SIGN_FILENAME,
				QUOTE_FLAG);

		String fileSuffix = "";
		if (fileName != null && !fileName.isEmpty()) {
			fileSuffix = getExtensionName(fileName);
		}

		String fileType = getFieldParam(fieldByte, charset, CONTENT_TYPE,
				AUTO_LINE);

		byte[] realFileBytes = getContent(fieldByte, charset);

		uploadFile.setFileName(fileName);
		uploadFile.setContentType(fileType);
		uploadFile.setSuffix(fileSuffix);
		uploadFile.setFileData(realFileBytes);
		uploadFile.setFieldName(fieldName);
		return uploadFile;
	}

	private static String getFieldParam(byte[] fieldByte, String charset,
			String nameFlag, String endFlag)
			throws UnsupportedEncodingException {

		byte[] filedAttrName = nameFlag.getBytes(charset);
		int point = 0;
		while (true) {
			if (fieldByte.length - point < filedAttrName.length) {
				break;
			}
			if (byteIsMapper(fieldByte, filedAttrName, point)) {
				break;
			} else {
				point++;
			}
		}

		int pos = point + filedAttrName.length;

		pos = pos > fieldByte.length ? fieldByte.length : pos;

		byte[] fileStart = Arrays.copyOfRange(fieldByte, pos, fieldByte.length);


		byte[] quoteBytes = endFlag.getBytes(charset);
		int point1 = 0;
		while (true) {
			if (fileStart.length - point1 < quoteBytes.length) {
				break;
			}
			if (byteIsMapper(fileStart, quoteBytes, point1)) {
				break;
			} else {
				point1++;
			}
		}


		byte[] nameBytes = Arrays.copyOfRange(fieldByte, pos, pos + point1);
		if (nameBytes == null || nameBytes.length == 0) {
			return null;
		}
		String result = new String(nameBytes, charset);

		result = result.trim();
		return result;
	}

	public static byte[] getContent(byte[] fieldByte, String charset)
			throws UnsupportedEncodingException, FileNotFoundException,
			IOException {

		byte[] autoLineByte = AUTO_LINE.getBytes(charset);
		int point = 0;
		while (true) {
			if (fieldByte.length - point < autoLineByte.length) {
				break;
			}
			if (byteIsMapper(fieldByte, autoLineByte, point)) {
				break;
			} else {
				point++;
			}
		}

		byte[] realFileByte = Arrays.copyOfRange(fieldByte, point
				+ autoLineByte.length, fieldByte.length - 2);

		return realFileByte;
	}

	public static boolean byteIsMapper(byte[] data, byte[] byteGiven, int point) {
		for (int i = 0; i < byteGiven.length; i++) {
			if (data[point + i] != byteGiven[i]) {
				return false;
			}
		}
		return true;
	}

	public static String getCharset(HttpServletRequest request) {
		String contentType = request.getContentType();
		String charset = getCharset(contentType, ';');
		if (charset == null || charset.isEmpty()) {
			charset = DEFAULT_ENCODING;
		}
		return charset;
	}

	public static byte[] getContentBytes(HttpServletRequest request)
			throws IOException {

		InputStream sis = request.getInputStream();
		int dataLength = request.getContentLength();

		byte[] dataBytes = new byte[dataLength];
		int byteRead = 0;
		int totalBytes = 0;

		while (totalBytes < dataLength) {
			byteRead = sis.read(dataBytes, totalBytes, dataLength);
			totalBytes += byteRead;
		}
		sis.close();
		return dataBytes;
	}

	public static String getBoundaryStr(HttpServletRequest request) {
		String contentType = request.getContentType();
		int lastIndex = contentType.lastIndexOf("=");

		String boundaryStr = "--"
				+ contentType.substring(lastIndex + 1, contentType.length());

		return boundaryStr;
	}

	public static String getExtensionName(String fileSuffix) {
		int format = fileSuffix.lastIndexOf(".");
		String extensionName = "";
		if (format >= 0) {
			extensionName = fileSuffix.substring(format);
		}
		return extensionName;
	}

	public static String getCharset(String contentType, char c) {
		if (contentType.indexOf("charset") == -1) {
			return null;
		} else {
			return contentType.substring(contentType.indexOf("charset") + 7,
					contentType.lastIndexOf(c));
		}
	}
}
